use crate::entity::dvc_metadata::{
    ActiveModel as DvcMetadataActiveModel, Column as DvcMetadataColumn, Entity as DvcMetadata, Model as DvcMetadataModel,
};
use crate::error::AppError;
use sea_orm::{ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, Set};
use std::path::Path;

pub struct DvcMetadataService {
    pub db: DatabaseConnection,
}

impl DvcMetadataService {
    pub fn new(db: DatabaseConnection) -> Self {
        Self { db }
    }

    // Create DVC metadata with actual file content parsing
    pub async fn create_dvc_metadata(
        &self,
        user_id: i64,
        dvc_file: &str,
    ) -> Result<DvcMetadataModel, AppError> {
        self.create_dvc_metadata_with_content(user_id, dvc_file, None).await
    }

    // Create DVC metadata with provided content (for upload scenarios)
    pub async fn create_dvc_metadata_with_content(
        &self,
        user_id: i64,
        dvc_file: &str,
        dvc_content: Option<&str>,
    ) -> Result<DvcMetadataModel, AppError> {
        let now = chrono::Utc::now();
        
        // Try to parse DVC file content to extract actual metadata
        let (data_filename, data_file_hash, data_file_size) = if let Some(content) = dvc_content {
            self.parse_dvc_content(content).unwrap_or_else(|_| {
                ("unknown".to_string(), "unknown".to_string(), None)
            })
        } else if let Ok(content) = std::fs::read_to_string(dvc_file) {
            self.parse_dvc_content(&content).unwrap_or_else(|_| {
                ("unknown".to_string(), "unknown".to_string(), None)
            })
        } else {
            ("unknown".to_string(), "unknown".to_string(), None)
        };

        let new_metadata = DvcMetadataActiveModel {
            user_id: Set(user_id),
            dvc_filename: Set(Path::new(dvc_file)
                .file_name()
                .and_then(|n| n.to_str())
                .unwrap_or(dvc_file)
                .to_string()),
            data_filename: Set(data_filename),
            data_file_hash: Set(data_file_hash),
            data_file_size: Set(data_file_size),
            dvc_file_path: Set(Some(dvc_file.to_string())),
            dvc_file_hash: Set(None), // Will be set later if needed
            project: Set(None), // Will be extracted from path if needed
            source: Set("dvc-upload".to_string()),
            created_at: Set(now.naive_utc()),
            updated_at: Set(now.naive_utc()),
            ..Default::default()
        };

        let model = new_metadata.insert(&self.db).await?;
        Ok(model)
    }

    // Parse DVC file content to extract metadata
    fn parse_dvc_content(&self, content: &str) -> Result<(String, String, Option<i64>), AppError> {
        let mut data_filename = "unknown".to_string();
        let mut data_file_hash = "unknown".to_string();
        let mut data_file_size: Option<i64> = None;
        
        let mut in_outs = false;
        let mut current_entry_lines = Vec::new();
        
        for line in content.lines() {
            if line == "outs:" {
                in_outs = true;
                continue;
            }
            
            if in_outs {
                if line.starts_with("- ") {
                    // Start of new output entry - process previous if exists
                    if !current_entry_lines.is_empty() {
                        self.parse_yaml_entry(&current_entry_lines, &mut data_filename, &mut data_file_hash, &mut data_file_size);
                        current_entry_lines.clear();
                    }
                    // Extract content after "- " marker if present
                    let after_marker = line[2..].trim();
                    if !after_marker.is_empty() {
                        current_entry_lines.push(after_marker.to_string());
                    }
                } else if line.starts_with("  ") || line.starts_with(" ") {
                    // Continuation of current entry (indented) - add whole line
                    current_entry_lines.push(line.trim().to_string());
                } else if line.is_empty() {
                    // Empty line - end of current entry
                    if !current_entry_lines.is_empty() {
                        self.parse_yaml_entry(&current_entry_lines, &mut data_filename, &mut data_file_hash, &mut data_file_size);
                        current_entry_lines.clear();
                    }
                } else {
                    // Non-indented line - end of current entry
                    if !current_entry_lines.is_empty() {
                        self.parse_yaml_entry(&current_entry_lines, &mut data_filename, &mut data_file_hash, &mut data_file_size);
                        current_entry_lines.clear();
                    }
                }
            }
        }
        
        // Parse the last entry if exists
        if !current_entry_lines.is_empty() {
            self.parse_yaml_entry(&current_entry_lines, &mut data_filename, &mut data_file_hash, &mut data_file_size);
        }
        
        Ok((data_filename, data_file_hash, data_file_size))
    }
    
    // Parse a YAML entry from DVC file (multiple lines)
    fn parse_yaml_entry(&self, lines: &[String], data_filename: &mut String, data_file_hash: &mut String, data_file_size: &mut Option<i64>) {
        for line in lines {
            if let Some((key, value)) = line.split_once(':') {
                let key = key.trim();
                let value = value.trim().to_string();
                
                match key {
                    "md5" => *data_file_hash = value,
                    "size" => *data_file_size = value.parse().ok(),
                    "path" => *data_filename = value,
                    "hash" => {
                        // This is hash type (md5, sha256), not the actual hash
                        // We can ignore it for now
                    }
                    _ => {}
                }
            } else if !line.contains(':') && data_filename == "unknown" {
                // Simple filename without metadata (unlikely in YAML format)
                *data_filename = line.to_string();
            }
        }
    }

    // Get DVC metadata by filename and user
    pub async fn get_dvc_metadata_by_filename_and_user(
        &self,
        filename: &str,
        user_id: i64,
    ) -> Result<Option<DvcMetadataModel>, AppError> {
        let metadata = DvcMetadata::find()
            .filter(DvcMetadataColumn::DvcFilename.eq(filename))
            .filter(DvcMetadataColumn::UserId.eq(user_id))
            .one(&self.db)
            .await?;

        Ok(metadata)
    }

    // Get DVC metadata by data file hash and user
    pub async fn get_dvc_metadata_by_data_hash_and_user(
        &self,
        hash: &str,
        user_id: i64,
    ) -> Result<Option<DvcMetadataModel>, AppError> {
        let metadata = DvcMetadata::find()
            .filter(DvcMetadataColumn::DataFileHash.eq(hash))
            .filter(DvcMetadataColumn::UserId.eq(user_id))
            .one(&self.db)
            .await?;

        Ok(metadata)
    }

    pub async fn update_dvc_file_path(
        &self,
        id: i64,
        file_path: &str,
        file_hash: Option<String>,
    ) -> Result<bool, AppError> {
        use crate::entity::dvc_metadata;
        use sea_orm::{ActiveModelTrait, EntityTrait, Set};

        if let Some(metadata) = dvc_metadata::Entity::find_by_id(id).one(&self.db).await? {
            let mut active_model: DvcMetadataActiveModel = metadata.into();
            active_model.dvc_file_path = Set(Some(file_path.to_string()));
            if let Some(hash) = file_hash {
                active_model.dvc_file_hash = Set(Some(hash));
            }
            active_model.updated_at = Set(chrono::Utc::now().naive_utc());

            active_model.update(&self.db).await?;
            Ok(true)
        } else {
            Ok(false)
        }
    }
}